package factory;

import factory.entity.Vehicle;

import java.util.HashMap;
import java.util.Map;

/**
 * 工厂模式
 * 目的: 逻辑封装, 通过公共接口, 提供对象实例化服务
 * 3种: 静态工厂, 工厂方法, 抽象工厂
 * 三者为从左到右发展
 * 根据情况, 可退化: 抽象工厂->工厂方法->静态工厂
 *
 * <p>
 * 简单工厂模式
 * 由一个工厂创建所有对象, 根据参数动态决定
 * 缺点: 需要修改来拓展
 * <p>
 * 改进的简单工厂: 新类使用时才被实例化, 保证其对扩展开放，同时对修改闭合
 * 1. 使用反射 注册新类和实例化(需要权限, 降低性能)
 * 2. 手动实现newInstance()
 *
 * @author insight
 * @since 2021/4/22
 */
public class StaticFactory {
    public enum VehicleType {
        /**
         * 3种交通工具
         */
        Bike, Car, Truck,
        ;
    }

    /**
     * 工厂类只负责实例化对象, 符合单一职责
     * 但是每增加一个新的VehicleType, 就需要修改工厂, 违反开闭原则
     */
    public static VehicleType getVehicle(VehicleType type) {
        if (type.equals(VehicleType.Bike)) {
            return VehicleType.Bike;
        }
        if (type.equals(VehicleType.Car)) {
            return VehicleType.Car;
        }
        if (type.equals(VehicleType.Truck)) {
            return VehicleType.Truck;
        }
        return null;
    }

    /* 第一种改进方法 */
    /**
     * 使用map来保存车的名称和对应类
     */
    private Map<String, Class<?>> registerVehicleClass = new HashMap<>(8);

    /**
     * 注册新类
     */
    public void registerVehicleClass(String vehicleName, Class<?> vehicleClass) {
        registerVehicleClass.put(vehicleName.toLowerCase(), vehicleClass);
    }

    /**
     * 工厂方法 获得对应类 并实例化
     */
    public Vehicle getVehicleClassByName(String vehicleName) throws IllegalAccessException, InstantiationException {
        Class<?> vehicleClass = registerVehicleClass.get(vehicleName.toLowerCase());
        return (Vehicle) vehicleClass.newInstance();
    }

    /* 第二种改进方法 */
    /**
     * 使用map来保存车的名称和对应类
     */
    private Map<String, Vehicle> registerVehicleType = new HashMap<>(8);

    /**
     * 注册新类
     */
    public void registerVehicleType(String vehicleName, Vehicle vehicle) {
        registerVehicleType.put(vehicleName.toLowerCase(), vehicle);
    }

    /**
     * 工厂方法 获得对应类 并实例化
     */
    public Vehicle getVehicleTypeByName(String vehicleName) {
        Vehicle vehicle = registerVehicleType.get(vehicleName.toLowerCase());
        return vehicle.newInstance();
    }
}